﻿#include "CClientMainModule.h"
#include "../game/MsgID.h"
#include "../game/MsgStruct.h"
#include "../game/MiscDefine.h"

#include <string.h>
#include <stdio.h>
#include <Windows.h>

#pragma warning(disable:4996)

static char _log_buf[1024] = {0};

#define CLINET_DEBUG_LOG(fmt, ...) { \
	memset(_log_buf, 0, sizeof(_log_buf)); \
	sprintf(_log_buf, "%s:%d fn:%s " fmt "\n", strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__); \
	WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), _log_buf, strlen(_log_buf), NULL, NULL); \
}

static ST_NOTIFY_VIEW_ATOMS_CHANGED* make_spare_part_display_data(CClientMainModule* thes) {
	int all_atoms_count = 0;
	int idx = 0;
	int len = 0;
	int i = 0;
	int x = 0;
	int y = 0;
	CMap* m = &thes->users_map;
	CMapIterator it;
	ST_NOTIFY_VIEW_ATOMS_CHANGED* v = 0;

	construct_cmap_iterator(&it);
	for (it.ptr = m->begin(m); it.ptr != m->end(m); it.next(&it)) {
		CUser* u = (CUser*)GET_VALUE_FROM_IT(it);
		if (!u) {
			continue;
		}
		if (u->current) {
			all_atoms_count += u->current->atom_cnt;
		}
		if (u->next) {
			all_atoms_count += u->next->atom_cnt;
		}
	}
	for (x = 0; x < ITEM_WIDTH_COUNT; ++x) { //32
		for (y = 0; y < ITEM_HEIGHT_COUNT; ++y) { //25
			if (!thes->piles[y * ITEM_WIDTH_COUNT + x]) {
				continue;
			}
			all_atoms_count++;
		}
	}

	len = sizeof(ST_NOTIFY_VIEW_ATOMS_CHANGED) + sizeof(TOS_ATOM) * (all_atoms_count - 1);
	v = (ST_NOTIFY_VIEW_ATOMS_CHANGED*)malloc(len);
	memset(v, 0, len);

	v->atoms_count = all_atoms_count;
	for (it.ptr = m->begin(m); it.ptr != m->end(m); it.next(&it)) {
		CUser* u = (CUser*)GET_VALUE_FROM_IT(it);
		if (!u) {
			continue;
		}
			
		if (u->current) {
			for (idx = 0; idx < u->current->atom_cnt; ++idx) {
				v->atoms[i].coord.x = u->current->atoms[idx].coord.x * ITEM_SIDE_LENGTH_IN_PIX + u->current->origin.x * ITEM_SIDE_LENGTH_IN_PIX + 40; //ITEM_SIDE_LENGTH_IN_PIX:18
				v->atoms[i].coord.y = u->current->atoms[idx].coord.y * ITEM_SIDE_LENGTH_IN_PIX + u->current->origin.y * ITEM_SIDE_LENGTH_IN_PIX + 195;
				v->atoms[i].data = u->current->atoms[idx].data;
				++i;
			}
		}
		if (u->next) {
			for (idx = 0; idx < u->next->atom_cnt; ++idx) {
				v->atoms[i].coord.x = u->next->atoms[idx].coord.x * ITEM_SIDE_LENGTH_IN_PIX + u->next_base_in_pix.x; //ITEM_SIDE_LENGTH_IN_PIX:18
				v->atoms[i].coord.y = u->next->atoms[idx].coord.y * ITEM_SIDE_LENGTH_IN_PIX + u->next_base_in_pix.y;
				v->atoms[i].data = u->next->atoms[idx].data;
				++i;
			}
		}
	}
	for (x = 0; x < ITEM_WIDTH_COUNT; ++x) { //32
		for (y = 0; y < ITEM_HEIGHT_COUNT; ++y) { //25
			if (!thes->piles[y * ITEM_WIDTH_COUNT + x]) {
				continue;
			}
			v->atoms[i].coord.x = x * ITEM_SIDE_LENGTH_IN_PIX + 40; //ITEM_SIDE_LENGTH_IN_PIX:18
			v->atoms[i].coord.y = y * ITEM_SIDE_LENGTH_IN_PIX + 195;
			v->atoms[i].data = thes->piles[y * ITEM_WIDTH_COUNT + x];
			++i;
		}
	}
	return v;
}

static int client_make_atoms_refresh(CClientMainModule* thes) {
	ST_NOTIFY_VIEW_ATOMS_CHANGED* v = make_spare_part_display_data(thes);
	v->msg.msg_id = MSG_ID_NOTIFY_VIEW_ATOMS_CHANGED;
	v->msg.data_len = sizeof(ST_NOTIFY_VIEW_ATOMS_CHANGED) + sizeof(TOS_ATOM) * (v->atoms_count - 1) - sizeof(CMsg);
	thes->view->sendMsg(thes->view, &v->msg);
	free(v);
	return 0;
}

static int ON_MODULE_MSG(CClientMainModule, MSG_ID_TEST_RES) {
	ST_TEST_RES* res = (ST_TEST_RES*)msg;
	TOS_POINT base = { 150, 100 };
    TOS_ATOM a[4] = { {{0, 0}, 1}, {{1, 0}, 1}, {{1, 1}, 1}, {{2, 1}, 1}};//z
    ST_NOTIFY_VIEW_ATOMS_CHANGED* v = 0;
    int len = sizeof(ST_NOTIFY_VIEW_ATOMS_CHANGED) + sizeof(TOS_ATOM) * (4 - 1);
    int i = 0;
    int j = 0;

    CLINET_DEBUG_LOG("data=%d", res->data[0]);
    for (i = 0; i < 4; ++i) {
        a[i].coord.x *= ITEM_SIDE_LENGTH_IN_PIX; //atom len&wid in pix==18
        a[i].coord.y *= ITEM_SIDE_LENGTH_IN_PIX;
        
        a[i].coord.x += base.x;
        a[i].coord.y += base.y;
    }
    v = (ST_NOTIFY_VIEW_ATOMS_CHANGED*)malloc(len);
    v->msg.msg_id = MSG_ID_NOTIFY_VIEW_ATOMS_CHANGED;
    v->atoms_count = 4;
    v->msg.data_len = len - sizeof(CMsg);
    memcpy(v->atoms, a, sizeof(a));
    thes->view->sendMsg(thes->view, &v->msg);
    free(v);
	return 0;
}

static int ON_MODULE_MSG(CClientMainModule, MSG_ID_NEW_CONNECT_RES) {
	ST_NEW_CONNECT_RES* res = (ST_NEW_CONNECT_RES*)msg;
	CMap* m = &thes->users_map;

	thes->game_user_count = res->game_user_count;
	thes->me->user_id = res->user_id;
	thes->me->next_base_in_pix.x = 100 + (thes->me->user_id - 1) * 200;
	thes->me->next_base_in_pix.y = 80;
	CLINET_DEBUG_LOG("user_id=%d game_user_count=%d", res->user_id, res->game_user_count);
	m->insert(m, (void*)res->user_id, (void*)thes->me);
	return 0;
}

static int ON_MODULE_MSG(CClientMainModule, MSG_ID_NOTIFY_NEW_USER_ADDED) {
	ST_NOTIFY_NEW_USER_ADDED* notify = (ST_NOTIFY_NEW_USER_ADDED*)msg;
	CMap* m = &thes->users_map;
	CUser* u = (CUser*)malloc(sizeof(CUser));

	CLINET_DEBUG_LOG("user_id=%d", notify->user_id);
	construct_user(u, notify->user_id, -1);
	u->next_base_in_pix.x = 100 + (u->user_id - 1) * 200;
	u->next_base_in_pix.y = 120;
	m->insert(m, (void*)notify->user_id, u);
	return 0;
}

static int ON_MODULE_MSG(CClientMainModule, MSG_ID_NOTIFY_CLIENT_SOCKET) {
	ST_NOTIFY_CLIENT_SOCKET* res = (ST_NOTIFY_CLIENT_SOCKET*)msg;
	CLINET_DEBUG_LOG("socket=0x%X", res->sock);
	thes->me->socket = res->sock;
	return 0;
}

static int ON_MODULE_MSG(CClientMainModule, MSG_ID_PREPARE_READY) {
	ST_USR_READY_REQ req = {{MSG_ID_USR_READY_REQ,  sizeof(req) - sizeof(CMsg)}, 0};
	CLINET_DEBUG_LOG("user_id=%d", thes->me->user_id);
	req.user_id = thes->me->user_id;
	thes->me->send_socket_msg(thes->me, &req.msg);
	return 0;
}

static int ON_MODULE_MSG(CClientMainModule, MSG_ID_USR_READY_RES) {
	ST_USR_READY_RES* res = (ST_USR_READY_RES*)msg;
	CLINET_DEBUG_LOG("user is ready. user_id=%d", thes->me->user_id);

	if (thes->me->user_id == res->user_id) {
		ST_SET_BTN cmd = {{MSG_ID_SET_BTN, sizeof(ST_SET_BTN) - sizeof(CMsg)}, 0};
		cmd.cmd_type = CMD_TYPE_BTN_HIDE;
		cmd.btn_id = IDB_START_GAME;
		thes->view->sendMsg(thes->view, &cmd.msg);
	}
	return 0;
}

static int ON_MODULE_MSG(CClientMainModule, MSG_ID_NOTIFY_KEY_DOWN) {
	ST_NOTIFY_KEY_DOWN* notify = (ST_NOTIFY_KEY_DOWN*)msg;
	ST_KEY_PRESS_REQ req = {{MSG_ID_KEY_PRESS_REQ, sizeof(req) - sizeof(CMsg)}, 0};

	CLINET_DEBUG_LOG("key=%d", notify->key);
	req.user_id = thes->me->user_id;
	switch (notify->key) {
	case VK_UP:
		req.which_key = TOS_UPKEY;
		break;
	case VK_LEFT:
		req.which_key = TOS_LEFTKEY;
		break;
	case VK_RIGHT:
		req.which_key = TOS_RIGHTKEY;
		break;
	case VK_DOWN:
		req.which_key = TOS_DOWNKEY;
		break;
	default:
		return -1;
	}
	thes->me->send_socket_msg(thes->me, &req.msg);
	return 0;
}

static int ON_MODULE_MSG(CClientMainModule, MSG_ID_GAMESTART_CMD) {
	ST_GAMESTART_CMD* notify = (ST_GAMESTART_CMD*)msg;

	CLINET_DEBUG_LOG("");
	thes->game.st = 1;
	return 0;
}

static int ON_MODULE_MSG(CClientMainModule, MSG_ID_USER_FIRST_NEXT) {
	ST_USER_FIRST_NEXT* n = (ST_USER_FIRST_NEXT*)msg;
	CMap* m = &thes->users_map;
	CUser* u = m->find(m, (void*)n->user_id);

	CLINET_DEBUG_LOG(" user_id=%d", n->user_id);
	free(u->next);
	u->next = (CSparePart*)malloc(sizeof(CSparePart));
	memcpy(u->next, &n->spare_part, sizeof(CSparePart));
	client_make_atoms_refresh(thes);
	return 0;
}

static int ON_MODULE_MSG(CClientMainModule, MSG_ID_USER_NEW_SPAREPART_NEXT) {
	ST_USER_NEW_SPAREPART_NEXT* n = (ST_USER_NEW_SPAREPART_NEXT*)msg;
	CMap* m = &thes->users_map;
	CUser* u = m->find(m, (void*)n->user_id);

	CLINET_DEBUG_LOG(" user_id=%d", n->user_id);
	free(u->current);
	u->current = u->next;
	u->current->origin.x = (u->user_id - 1) * 16 + 8;
	u->current->origin.y = 0;
	CLINET_DEBUG_LOG("user(%d) current spare part is refreshed. origin[%d, %d]", u->user_id, u->current->origin.x, u->current->origin.y);

	u->next = (CSparePart*)malloc(sizeof(CSparePart));
	memcpy(u->next, &n->spare_part, sizeof(CSparePart));
	client_make_atoms_refresh(thes);
	return 0;
}


static int ON_MODULE_MSG(CClientMainModule, MSG_ID_KEY_PRESS_RES) {
	ST_KEY_PRESS_RES* res = (ST_KEY_PRESS_RES*)msg;
	CMap* m = &thes->users_map;
	CUser* u = m->find(m, (void*)res->user_id);

	CLINET_DEBUG_LOG(" user_id=%d, which_key=%d", res->user_id, res->which_key);
	switch (res->which_key) {
	case TOS_LEFTKEY:
		spare_part_move_left(u->current);
		break;
	case TOS_RIGHTKEY:
		spare_part_move_right(u->current);
		break;
	case TOS_DOWNKEY:
		spare_part_move_down(u->current);
		break;
	case TOS_UPKEY:
		spare_part_rotate(u->current);
		break;
	default:
		return -1;
	}
	CLINET_DEBUG_LOG("u[%d] after key action origin[%d, %d]",u->user_id, u->current->origin.x, u->current->origin.y);
	CLINET_DEBUG_LOG("data[%d, %d][%d, %d][%d, %d][%d, %d]", u->current->atoms[0].coord.x, u->current->atoms[0].coord.y,
		u->current->atoms[1].coord.x, u->current->atoms[1].coord.y,
		u->current->atoms[2].coord.x, u->current->atoms[2].coord.y,
		u->current->atoms[3].coord.x, u->current->atoms[3].coord.y);
	client_make_atoms_refresh(thes);
	return 0;
}

static int ON_MODULE_MSG(CClientMainModule, MSG_ID_DO_PILE_CMD) {
	ST_DO_PILE_CMD* cmd = (ST_DO_PILE_CMD*)msg;
	CMap* m = &thes->users_map;
	CUser* u = m->find(m, (void*)cmd->user_id);

	CLINET_DEBUG_LOG("user_id=%d", cmd->user_id);
	do_pile(u->current, thes->piles);
	client_make_atoms_refresh(thes);
	return 0;
}

int set_row_atom_shape(CClientMainModule* thes, int row, int shape) {
	int x = 0;
	for (x = 0; x < ITEM_WIDTH_COUNT; ++x) {
		set_atom_data_shape(&thes->piles[row * ITEM_WIDTH_COUNT + x], shape);
	}
	return 0;
}

int erase_full_row_animate(CClientMainModule* thes, CMsg* msg) {
	int i = 0; 
	int x = 0;
	int y = 0;
	int cnt = 0;
	ST_ERASE_FULL_ROW_CMD* cmd = (ST_ERASE_FULL_ROW_CMD*)msg;
	for (i = 0; i < 2; ++i) {
		for (cnt = 0; cnt < cmd->row_cnt; ++cnt) {
			set_row_atom_shape(thes, cmd->which_row[cnt], ATOM_SHAPE_DIAMOND);
		}
		client_make_atoms_refresh(thes);
		Sleep(200);

		for (cnt = 0; cnt < cmd->row_cnt; ++cnt) {
			set_row_atom_shape(thes, cmd->which_row[cnt], ATOM_SHAPE_NORMAL);
		}
		client_make_atoms_refresh(thes);
		Sleep(200);
	}
	return 0;
}

static int ON_MODULE_MSG(CClientMainModule, MSG_ID_ERASE_FULL_ROW_CMD) {
	ST_ERASE_FULL_ROW_CMD* cmd = (ST_ERASE_FULL_ROW_CMD*)msg;
	CMap* m = &thes->users_map;
	int i = 0;
	
	CLINET_DEBUG_LOG("row_cnt=%d", cmd->row_cnt);
	erase_full_row_animate(thes, msg);
	for (i = 0; i < cmd->row_cnt; ++i) {
		do_erase_full_row(thes->piles, cmd->which_row[i]);
	}
	client_make_atoms_refresh(thes);
	return 0;
}

static int ON_MODULE_MSG(CClientMainModule, MSG_ID_DEBUG_PILE1) {
	int x = 0;
	int y = 0;
	CLINET_DEBUG_LOG("");
	for (y = ITEM_HEIGHT_COUNT - 2; y < ITEM_HEIGHT_COUNT; ++y) {
		for (x = 0; x < ITEM_WIDTH_COUNT - 1; ++x) {
			thes->piles[y * ITEM_WIDTH_COUNT + x] = ATOM_SHAPE_NORMAL;
		}
	}
	client_make_atoms_refresh(thes);
	return 0;
}

static int cclient_main_reg_func_on_msgid(CModule* thes) {
	REG_MODULE_MSG_FUNC(CClientMainModule, thes, MSG_ID_TEST_RES);
	REG_MODULE_MSG_FUNC(CClientMainModule, thes, MSG_ID_NEW_CONNECT_RES);
	REG_MODULE_MSG_FUNC(CClientMainModule, thes, MSG_ID_NOTIFY_CLIENT_SOCKET);
	REG_MODULE_MSG_FUNC(CClientMainModule, thes, MSG_ID_PREPARE_READY);
	REG_MODULE_MSG_FUNC(CClientMainModule, thes, MSG_ID_USR_READY_RES);
	REG_MODULE_MSG_FUNC(CClientMainModule, thes, MSG_ID_NOTIFY_KEY_DOWN);
	REG_MODULE_MSG_FUNC(CClientMainModule, thes, MSG_ID_GAMESTART_CMD);	
	REG_MODULE_MSG_FUNC(CClientMainModule, thes, MSG_ID_USER_FIRST_NEXT);
	REG_MODULE_MSG_FUNC(CClientMainModule, thes, MSG_ID_NOTIFY_NEW_USER_ADDED);
	REG_MODULE_MSG_FUNC(CClientMainModule, thes, MSG_ID_USER_NEW_SPAREPART_NEXT);
	REG_MODULE_MSG_FUNC(CClientMainModule, thes, MSG_ID_KEY_PRESS_RES);	
	REG_MODULE_MSG_FUNC(CClientMainModule, thes, MSG_ID_DO_PILE_CMD);
	REG_MODULE_MSG_FUNC(CClientMainModule, thes, MSG_ID_ERASE_FULL_ROW_CMD);
	REG_MODULE_MSG_FUNC(CClientMainModule, thes, MSG_ID_DEBUG_PILE1);
    return 0;
}

int construct_client_main(CClientMainModule* thes, const char* module_name) {
	CMap* m = &thes->users_map;
	CUser* me = (CUser*)malloc(sizeof(CUser));
	memset(me, 0, sizeof(CUser));
	construct_module(&thes->parent, module_name);
	
	construct_map(m, INT_KEY_CMP_FUNC);

	construct_user(me, 0, 0);
	thes->me = me;
	memset(thes->piles, 0, sizeof(thes->piles));
	construct_toris_game(&thes->game);
	cclient_main_reg_func_on_msgid(AS_MODULE(thes));
	AS_MODULE(thes)->run(AS_MODULE(thes), USE_DEFAULT_MODULE_ENTRY);
	return 0;
}

